package spring.cloud.eurekaclientproduct.service.impl;

import com.netflix.discovery.util.StringUtil;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.annotation.ObservableExecutionMode;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import com.netflix.hystrix.contrib.javanica.command.AsyncResult;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;

import org.springframework.web.client.RestTemplate;
import rx.Observable;
import spring.cloud.eurekaclientproduct.cmd.UserExpCommand;
import spring.cloud.eurekaclientproduct.cmd.UserGetCommand;
import spring.cloud.eurekaclientproduct.cmd.UserPutCommand;
import spring.cloud.eurekaclientproduct.cmd.UserTimeoutCommand;
import spring.cloud.eurekaclientproduct.service.UserFacade;
import com.spring.cloud.commonutils.vo.UserInfo;
import com.spring.cloud.commonutils.vo.ResultMessage;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;


@Service
public class UserFacadeImpl implements UserFacade {

    @Autowired
    private RestTemplate restTemplate;


    @Override
    @HystrixCommand(fallbackMethod = "fallback1")
    public ResultMessage timeout() {
        String url = "http://eureka-client-user/hystrix/timeout";
        return restTemplate.getForObject(url, ResultMessage.class);
    }

    @Override
    @HystrixCommand(fallbackMethod = "fallback2")
    public ResultMessage exp(String msg) {
        String url = "http://eureka-client-user/hystrix/exp/{msg}";
        return restTemplate.getForObject(url,ResultMessage.class,msg);
    }

    //代码实现 命令
    @Override
    public ResultMessage timeout2() {

        HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("userGroup");
        com.netflix.hystrix.HystrixCommand.Setter setter
                = com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(groupKey);
        UserTimeoutCommand userCmd = new UserTimeoutCommand(setter,restTemplate);
        //同步执行
        return userCmd.execute();

         //异步执行
//        Future<ResultMessage> future = userCmd.queue();
//        try{
//          return future.get();
//        }catch (Exception ex){
//          return userCmd.getFallback();
//        }

    }

    @Override
    @HystrixCommand(fallbackMethod = "fallback1")
    public Future<ResultMessage> asyncTimeout() {
        return new AsyncResult<ResultMessage>(){
            @Override
            public ResultMessage invoke() {
                String url = "http://eureka-client-user/hystrix/timeout";
                return restTemplate.getForObject(url,ResultMessage.class);
            }
        };
    }


    //代码实现 观察者模式
    @Override
    public List<ResultMessage> exp2(String[] params){
        HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("userGroup");
        com.netflix.hystrix.HystrixObservableCommand.Setter setter = com.netflix.hystrix.HystrixObservableCommand.Setter.withGroupKey(groupKey);
        UserExpCommand userCmd = new UserExpCommand(setter,restTemplate,params);
        List<ResultMessage> resList = new ArrayList<>();
        Observable<ResultMessage> observable = userCmd.observe();

        //冷观察模式
//        Observable<ResultMessage> observable = userCmd.toObservable();
        observable.forEach((ResultMessage resultMsg)->{
            resList.add(resultMsg);
        });
        return resList;
    }

    //注解实现观察者模式
    @Override
    @HystrixCommand(fallbackMethod = "fallback3",observableExecutionMode = ObservableExecutionMode.EAGER)
    public Observable<ResultMessage> asyncExp(String[] params){
        String url = "http://eureka-user-client/hystrix/exp/{msg}";
        Observable.OnSubscribe<ResultMessage> onSubs = (resSubs)->{
            try{
                int count = 0;
                if(!resSubs.isUnsubscribed()){
                    for(String param:params){
                        count++;
                        System.out.println("第【"+count+"】次发送");
                        ResultMessage resMsg = restTemplate.getForObject(url,ResultMessage.class,param);
                        resSubs.onNext(resMsg);
                    }
                    resSubs.onCompleted();
                }
            }catch (Exception ex){
                resSubs.onError(ex);
            }
        };
        return Observable.unsafeCreate(onSubs);
    }



    public ResultMessage fallback1(){
        return new ResultMessage(false,"超时了");
    }

    public ResultMessage fallback2(String msg){
        return new ResultMessage(false,"调用产生异常，参数:"+msg);
    }

    public ResultMessage fallback3(String[] params,Throwable ex){

        ex.printStackTrace();
        return new ResultMessage(false,"调用产生异常了，参数："+params);
    }


    @Override
    public UserInfo testUserInfo(Long id) {
        //这里简化HystrixRequestContext对象 可以在filter里 初始化HystrixRequestContext对象
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        UserGetCommand ugc1 = new UserGetCommand(restTemplate, id);
        UserGetCommand ugc2 = new UserGetCommand(restTemplate, id);
        UserGetCommand ugc3 = new UserGetCommand(restTemplate, id);
        UserPutCommand upc = new UserPutCommand(restTemplate, 1L, "user_name_update", "note_update");
        try {
            ugc1.execute(); //进行缓存
            ugc2.execute(); //从缓存中获取 不会执行run方法
            upc.execute();  //做更新之后清除缓存
            return ugc3.execute();  //有进行了缓存
        } finally {
            context.close();
        }

    }
    @Override
    @CacheResult(cacheKeyMethod = "getCacheKey")    //进行缓存
    @HystrixCommand(commandKey = "user_get")
    public UserInfo getUserInfo(@CacheKey Long id){ //缓存的key 为id
        String url = "http://eureka-client-user/user/info/{id}";
        System.out.println("获取用户"+id);
        return restTemplate.getForObject(url,UserInfo.class,id);
    }

    @Override
    @CacheRemove(commandKey = "user_get",cacheKeyMethod = "getCacheKey")    //cacheKeyMethod 是指向的是函数的方法名
    @HystrixCommand
    public UserInfo updateUserInfo(UserInfo user){
        String url ="http://eureka-client-user/user/info";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_VALUE));
        HttpEntity<UserInfo> request = new HttpEntity<>(user,headers);
        restTemplate.put(url,request);
        return user;

    }

    private static final String CACHE_PREFIX = "user_";

    public String getCacheKey(Long id){
        return CACHE_PREFIX + id;
    }
    public String getCacheKey(UserInfo userInfo){
        return CACHE_PREFIX + userInfo.getId();
    }

    @Override
    public UserInfo getUser(Long id) {
        String url = "http://eureka-client-user/user/info/{id}";
        return restTemplate.getForObject(url,UserInfo.class,id);
    }


    @Override
    public List<UserInfo> findUsers(Long[] ids) {
        String url = "http://eureka-client-user/user/infoes/{ids}";
        String strIds = StringUtils.join(ids,",");
        System.out.println("批量发送的请求=》"+strIds);
        ParameterizedTypeReference<List<UserInfo>> responseType = new ParameterizedTypeReference<List<UserInfo>>(){};
        ResponseEntity<List<UserInfo>> userEntity = restTemplate.exchange(url, HttpMethod.GET,null,responseType,ids);
        return userEntity.getBody();
    }

    @Override
    @HystrixCollapser(collapserKey = "userGroup",batchMethod = "findUsers2",
    scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
    collapserProperties = {
            @HystrixProperty(name = "timerDelayInMilliseconds",value = "50"),
            @HystrixProperty(name = "maxRequestsInBatch",value="3")
    })
    public Future<UserInfo> getUser2(Long id) {
        return null;
    }

    @Override
    @HystrixCommand(commandKey = "userGroup")
    public List<UserInfo> findUsers2(Long[] ids) {
        String url ="http://eureka-client-user/user/infoes/{ids}";
        String strIds = StringUtils.join(ids,",");
        System.out.println("批量发送的请求=》"+strIds);
        ParameterizedTypeReference<List<UserInfo>> responseType = new ParameterizedTypeReference<List<UserInfo>>(){};
        ResponseEntity<List<UserInfo>> userEntity = restTemplate.exchange(url, HttpMethod.GET,null,responseType,ids);
        return userEntity.getBody();
    }

}
